package TEC;

import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;

import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Tec {

    public static KeyCodeTools keyCodeTools = new KeyCodeTools();
    public static DrawLine drawLine = new DrawLine();
    public static CodeTable codeTable = new CodeTable();
    public TextArea textArea1;
    public TextArea textArea2;
    public Integer PC;
    public Map<Integer, String> memory = new ConcurrentHashMap<>();
    public Map<Integer, String> port = new ConcurrentHashMap<Integer, String>() {{
        put(80, "0000");
        put(81, "0000");
        put(90, "0000");
        put(91, "0000");
    }};
    public int cursor;
    public int status = 0; //0 初始状态 , 1 A命令 ,2 E命令，3 G命令，4 U命令
    public String R[] = {
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
            "0000",
    };
    public Stack sp = new Stack();
    public String C = "0";
    public String Z = "1";
    private SubRoutine subRoutine;
    private String currentLine = "";

    public void reset() {
        port.clear();
        port.put(80, "0000");
        port.put(81, "0000");
        port.put(90, "0000");
        port.put(91, "0000");
        status = 0;
        C = "0";
        Z = "1";
        if (subRoutine != null && subRoutine.thread.isAlive()) {
            subRoutine.thread.interrupt();
        }
    }

    public void start() {
        drawLine.newLine(textArea1, "Tec-XP Simulation Program\nVersion 1.0 2017.12.23\nComputer Architectur DUT\n>");
        drawLine.newLine(textArea2, "Tec-XP Simulation Program\nVersion 1.0 2017.12.23\nComputer Architectur DUT\n>");
    }

    public Tec(TextArea textArea1, TextArea textArea2) {
        this.textArea1 = textArea1;
        this.textArea2 = textArea2;
    }

    //监控命令正则表达式
    private String regExMonitor = "[aegu] [0-9]{4}$";
    private Pattern patternMonitor = Pattern.compile(regExMonitor, Pattern.CASE_INSENSITIVE);
    private String regExTwoParameterOrder = "^(ADD|SUB|AND|CMP|XOR|TEST|OR|MVRR) R(1[0-5]|\\d{1}|0),R(1[0-5]|\\d{1}|0)$";
    private Pattern patternTwoParameterOrder = Pattern.compile(regExTwoParameterOrder, Pattern.CASE_INSENSITIVE);
    private String regExOneParamterOrder = "^(DEC|INC|SHL|SHR|PUSH|POP) R(1[0-5]|\\d{1}|0)$";
    private Pattern patternOneParameterOrder = Pattern.compile(regExOneParamterOrder, Pattern.CASE_INSENSITIVE);
    private String regExOffsetOrder = "^(JR|JRC|JRNC|JRZ|JRNZ) [0-9A-F]{1,4}$";
    private Pattern patternOffsetOrder = Pattern.compile(regExOffsetOrder, Pattern.CASE_INSENSITIVE);
    private String regExPortOrder = "^(IN|OUT) (80|81|90|91)";
    private Pattern patternPortOrder = Pattern.compile(regExPortOrder, Pattern.CASE_INSENSITIVE);
    private String regExNoneOrder = "(EI|DI|RET|IRET)";
    private Pattern patternNoneOrder = Pattern.compile(regExNoneOrder, Pattern.CASE_INSENSITIVE);
    private String regExMVRD = "^MVRD R(1[0-5]|\\d{1}|0),[0-9A-F]{1,4}$";
    private Pattern patternMVRD = Pattern.compile(regExMVRD, Pattern.CASE_INSENSITIVE);
    private String regExCALAJMPA = "^(CALA|JMPA) [0-9A-F]{1,4}$";
    private Pattern patternCALAJMPA = Pattern.compile(regExCALAJMPA, Pattern.CASE_INSENSITIVE);
    private String regExCode = "^[0-9A-F]{1,4}$";
    private Pattern patternCode = Pattern.compile(regExCode, Pattern.CASE_INSENSITIVE);


    public void whichScreen(javafx.scene.input.KeyEvent event, int screenNum) {
        if (screenNum == 80) {
            keyDown(event, textArea1, screenNum);
        } else if ((screenNum == 90)) {
            keyDown(event, textArea2, screenNum);
        }
    }

    //键盘监听函数
    public void keyDown(javafx.scene.input.KeyEvent event, TextArea textArea, int portNum) {
        if (subRoutine == null || !subRoutine.thread.isAlive()) {
            if (keyCodeTools.IsNumLetter(event.getCode())) {
                currentLine += event.getText();
                drawLine.AddCharToLastLine(textArea, event.getText());
            } else if (event.getCode() == KeyCode.SPACE || event.getCode() == KeyCode.COMMA) {
                currentLine += event.getText();
                drawLine.AddCharToLastLine(textArea, event.getText());
            } else if (event.getCode() == KeyCode.BACK_SPACE) {
                if (currentLine.length() != 0) {
                    currentLine = currentLine.substring(0, currentLine.length() - 1);
                    drawLine.RecCharAtLastLine(textArea);
                }
            } else if (event.getCode() == KeyCode.ENTER) {
                if (status == 0) {
                    ParseInitOrder(textArea);
                    currentLine = "";
                } else if (status == 1) {
                    ParseAOrder(textArea);
                    currentLine = "";
                } else if (status == 2) {
                    ParseEOrder(textArea);
                    currentLine = "";
                }
            }
        } else {
            if (event.isControlDown() && event.getCode() == KeyCode.C) {
                subRoutine.thread.interrupt();
            }
            if (keyCodeTools.IsNum(event.getCode())) {
                if (event.getCode().toString().charAt(0) == 68) {
                    port.put(portNum, Integer.toHexString(event.getCode().toString().charAt(5)));
                    setPort(portNum);
                } else if (event.getCode().toString().charAt(0) == 78) {
                    port.put(portNum, Integer.toHexString(event.getCode().toString().charAt(6)));
                    setPort(portNum);
                }
            } else {
                port.put(portNum, Integer.toHexString(event.getCode().toString().charAt(0)));
                setPort(portNum);
            }
        }
    }

    private void setPort(int portNum) {
        String temp = CompleteBinary(Integer.toString(Integer.parseInt(port.get(portNum + 1), 16), 2));
        port.put(portNum + 1, Integer.toHexString(Integer.parseInt(temp.substring(0, 1) + "1" + temp.substring(2), 2)).toUpperCase());

    }

    public String CompleteBinary(String binary) {
        String string = binary;
        for (int i = binary.length(); i < 16; i++) {
            string = "0" + string;
        }
        return string;
    }

    //分析监控指令
    private void ParseInitOrder(TextArea textArea) {
        if (command_legal()) {
            if (currentLine.isEmpty()) {
                drawLine.newLine(textArea, ">");
            } else {
                PC = Integer.parseInt(currentLine.substring(2, 6), 16);
                if (currentLine.charAt(0) == 'A' || currentLine.charAt(0) == 'a') {//解析到有A命令
                    drawLine.newLine(textArea, PC);
                    status = 1;
                } else if (currentLine.charAt(0) == 'E' || currentLine.charAt(0) == 'e') {//解析到有E命令
                    drawLine.newLine(textArea, PC);
                    status = 2;
                } else if (currentLine.charAt(0) == 'G' || currentLine.charAt(0) == 'g') {// 解析到有G命令
                    status = 3;
                    G_Run(textArea);
                    status = 0;
                } else if (currentLine.charAt(0) == 'U' || currentLine.charAt(0) == 'u') {// 解析到有U命令
                    status = 4;
                    U_Show(textArea);
                    status = 0;
                }
            }
        } else {
            drawLine.Error(textArea);
            drawLine.newLine(textArea, ">");
        }
    }

    // 监控命令合法性
    private boolean command_legal() {
        if (currentLine.isEmpty())
            return true;
        Matcher matcher = patternMonitor.matcher(currentLine);
        return matcher.matches();
    }

    //分析A命令
    private void ParseAOrder(TextArea textArea) {
        if (currentLine.isEmpty()) {
            status = 0;
            drawLine.newLine(textArea, ">");
        } else {
            if (AToMemory()) {
                drawLine.newLine(textArea, PC);
            } else {
                drawLine.Error(textArea);
                drawLine.newLine(textArea, PC);
            }
        }
    }

    private boolean AToMemory() {
        String orderCode = "";
        Matcher matcher = patternTwoParameterOrder.matcher(currentLine);
        if (matcher.matches()) {
            orderCode = codeTable.getCodeWithTwoParameter(currentLine);
        } else {
            matcher = patternOneParameterOrder.matcher(currentLine);
            if (matcher.matches()) {
                orderCode = codeTable.getCodeWithOneParameter(currentLine);
            } else {
                matcher = patternOffsetOrder.matcher(currentLine);
                if (matcher.matches()) {
                    orderCode = codeTable.getCodeOfOffset(currentLine, PC);
                    if (orderCode.isEmpty()) {
                        return false;
                    }
                } else {
                    matcher = patternPortOrder.matcher(currentLine);
                    if (matcher.matches()) {
                        orderCode = codeTable.getCodeOfPortAndCALAAndJMPA(currentLine);
                    } else {
                        matcher = patternNoneOrder.matcher(currentLine);
                        if (matcher.matches()) {
                            orderCode = codeTable.getCode(currentLine);
                        } else {
                            matcher = patternMVRD.matcher(currentLine);
                            if (matcher.matches()) {
                                orderCode = codeTable.getCodeOfMRVD(currentLine);
                                memory.put(PC++, orderCode.substring(0, 3));
                                memory.put(PC++, orderCode.substring(3));
                                return true;
                            } else {
                                matcher = patternCALAJMPA.matcher(currentLine);
                                if (matcher.matches()) {
                                    orderCode = codeTable.getCodeOfPortAndCALAAndJMPA(currentLine);
                                    memory.put(PC++, orderCode.substring(0, 2));
                                    memory.put(PC++, orderCode.substring(2));
                                    return true;
                                }
                                return false;
                            }
                        }
                    }
                }
            }
        }
        System.out.println("save as " + orderCode);
        memory.put(PC++, orderCode);

        // 遍历map
//        Iterator iterator = memory.entrySet().iterator();
//        for (int i=0;i<memory.size();i++){
//            Map.Entry entry = (Map.Entry)iterator.next();
//            System.out.println(entry.getKey()+" ; "+entry.getValue());
//        }

        return true;
    }

    private void U_Show(TextArea textArea) {
        int pc = PC;
        for (int i = pc; ; i++) {
            if (memory.get(i) == null) {
                break;
            } else {
                String order = memory.get(i).substring(0, 2);
                if (order.equalsIgnoreCase("88") || order.equalsIgnoreCase("CE") || order.equalsIgnoreCase("80")) {
                    String Order = codeTable.getOrderByCode(memory.get(i), i) + "\t";
                    Order += memory.get(++i);
                    drawLine.newLine(textArea, i - 1, Order);
                } else {
                    drawLine.newLine(textArea, i, codeTable.getOrderByCode(memory.get(i), i));
                }
            }
        }
        drawLine.newLine(textArea, ">");
    }

    private void ParseEOrder(TextArea textArea) {
        if (currentLine.isEmpty()) {
            status = 0;
            drawLine.newLine(textArea, ">");
        } else {
            Matcher matcher = patternCode.matcher(currentLine);
            if (matcher.matches()) {
                memory.put(PC++, currentLine);
                drawLine.newLine(textArea, ">");
            } else {
                drawLine.Error(textArea);
                drawLine.newLine(textArea, ">");
            }
        }
    }


    private void G_Run(TextArea textArea) {
        cursor = PC;
        drawLine.newLine(textArea, "");
        subRoutine = new SubRoutine(this);

    }


}
